package com.jzo2o.orders.manager.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.jzo2o.api.trade.TradingApi;
import com.jzo2o.api.trade.dto.response.TradingResDTO;
import com.jzo2o.api.trade.enums.TradingStateEnum;
import com.jzo2o.common.constants.UserType;
import com.jzo2o.common.expcetions.DBException;
import com.jzo2o.common.expcetions.ForbiddenOperationException;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.orders.base.enums.OrderRefundStatusEnum;
import com.jzo2o.orders.base.enums.OrderStatusEnum;
import com.jzo2o.orders.base.mapper.OrdersCanceledMapper;
import com.jzo2o.orders.base.model.domain.Orders;
import com.jzo2o.orders.base.model.domain.OrdersCanceled;
import com.jzo2o.orders.base.model.domain.OrdersRefund;
import com.jzo2o.orders.base.model.dto.OrderUpdateStatusDTO;
import com.jzo2o.orders.base.service.IOrdersCommonService;
import com.jzo2o.orders.manager.model.dto.OrderCancelDTO;
import com.jzo2o.orders.manager.service.IOrdersCanceledService;
import com.jzo2o.orders.manager.service.IOrdersManagerService;
import com.jzo2o.orders.manager.service.IOrdersRefundService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 * <p>
 * 订单取消服务实现类
 * </p>
 * @author itcast
 * @since 2023-08-19
 */
@Service
public class OrdersCanceledServiceImpl extends ServiceImpl<OrdersCanceledMapper, OrdersCanceled> implements IOrdersCanceledService {
    @Resource
    private IOrdersCommonService ordersCommonService;
    @Resource
    private IOrdersManagerService ordersManagerService;
    @Resource
    private IOrdersRefundService ordersRefundService;
    @Resource
    private TransactionTemplate transactionTemplate;
    @Resource
    private TradingApi tradingApi;

    @Override
    public void cancel(OrderCancelDTO orderCancelDTO) {
        Long orderId = orderCancelDTO.getId();
        Orders orders = ordersManagerService.getById(orderId);
        if (ObjectUtils.isEmpty(orders)) {
            throw new ForbiddenOperationException("订单不存在无法取消");
        }
        // 补充前端无法确定的字段
        orderCancelDTO.setServeStartTime(orders.getServeStartTime());
        orderCancelDTO.setRealPayAmount(/*orders.getRealPayAmount() 测试统一使用0.01元*/ new BigDecimal("0.01"));
        orderCancelDTO.setCityCode(orders.getCityCode());
        orderCancelDTO.setTradingOrderNo(orders.getTradingOrderNo());

        Integer ordersStatus = orders.getOrdersStatus();
        if (OrderStatusEnum.NO_PAY.getStatus().equals(ordersStatus)) {
            // 1. 未支付订单 -> 取消状态
            cancelNoPayOrder(orderCancelDTO);
        } else if (OrderStatusEnum.DISPATCHING.getStatus().equals(ordersStatus)) {
            // 2. 已支付订单 -> 关闭状态 + 退款
            cancelDispatchingOrder(orderCancelDTO);
        } else {
            throw new ForbiddenOperationException("订单无法取消");
        }
    }

    @Override
    public void cancelPayOverTimeOrder(Orders order) {
        // 二次确认防止在此期间支付
        Long tradingOrderNo = order.getTradingOrderNo();
        if (ObjectUtils.isNotEmpty(tradingOrderNo)) {
            // 再次请求防止已支付
            TradingResDTO tradingResDTO = tradingApi.findTradResultByTradingOrderNo(tradingOrderNo);
            if (ObjectUtils.isNotEmpty(tradingResDTO) && tradingResDTO.getTradingState() == TradingStateEnum.YJS) {
                return;
            }
        }

        cancelNoPayOrder(OrderCancelDTO.builder()
                .id(order.getId())
                .cancelReason("订单超时未支付自动取消")
                .currentUserId(-1L)
                .currentUserName("SYSTEM")
                .currentUserType(UserType.SYSTEM)
                .build());
    }

    private void cancelNoPayOrder(OrderCancelDTO orderCancelDTO) {
        transactionTemplate.executeWithoutResult(status -> {
            if (!saveCancelInfo(orderCancelDTO)) {
                throw new DBException("订单取消表记录表更新失败");
            }

            if (!ordersCommonService.updateStatus(OrderUpdateStatusDTO.builder()
                    .id(orderCancelDTO.getId())
                    .originStatus(OrderStatusEnum.NO_PAY.getStatus())
                    .targetStatus(OrderStatusEnum.CANCELED.getStatus())
                    .build())) {
                throw new DBException("订单表订单状态更新失败");
            }
        });
    }

    private void cancelDispatchingOrder(OrderCancelDTO orderCancelDTO) {
        Long orderId = orderCancelDTO.getId();
        // 更新数据库信息
        // 1. 订单状态信息 2. 取消记录表 2. 退款记录表
        transactionTemplate.executeWithoutResult(status -> {
            if (!saveCancelInfo(orderCancelDTO)) {
                throw new DBException("订单取消记录表更新失败");
            }

            if (!ordersRefundService.saveRefundInfo(orderCancelDTO)) {
                throw new DBException("订单退款记录表更新失败");
            }

            if (!ordersCommonService.updateStatus(OrderUpdateStatusDTO.builder()
                    .id(orderId)
                    .originStatus(OrderStatusEnum.DISPATCHING.getStatus())
                    .targetStatus(OrderStatusEnum.CLOSED.getStatus())
                    .refundStatus(OrderRefundStatusEnum.REFUNDING.getStatus())
                    .build())) {
                throw new DBException("订单表订单状态更新失败");
            }
        });

        // 启动新线程执行退款任务
        new Thread(() -> ordersRefundService
                .refundOrderByRefundRecord(OrdersRefund.builder()
                        .id(orderId)
                        .tradingOrderNo(orderCancelDTO.getTradingOrderNo())
                        .realPayAmount(orderCancelDTO.getRealPayAmount())
                        .build()))
                .start();
    }

    private Boolean saveCancelInfo(OrderCancelDTO orderCancelInfo) {
        return SqlHelper.retBool(baseMapper.insert(OrdersCanceled.builder()
                .id(orderCancelInfo.getId())
                .cancellerId(orderCancelInfo.getCurrentUserId())
                .cancelerName(orderCancelInfo.getCurrentUserName())
                .cancellerType(orderCancelInfo.getCurrentUserType())
                .cancelReason(orderCancelInfo.getCancelReason())
                .cancelTime(LocalDateTime.now())
                .build()));
    }
}